package org.tamal.classinfo;

import java.io.DataInputStream;
import java.io.IOException;

/**
 * This class represents a frame in StackMapTable.
 * @author Tamal Kanti Nath
 *
 */
public final class StackMapFrame {
    private final int frameType;
    private int offsetDelta;
    private VerificationType[] locals;
    private VerificationType[] stack;

    /**
     * Creates StackMapFrame which is an element of StackMapTable attribute.
     * @param in the DataInputStream
     * @throws IOException exception
     */
    private StackMapFrame(DataInputStream in) throws IOException {
        frameType = in.readUnsignedByte();
        if (0 <= frameType && frameType <= 63) {
            // same_frame
            return;
        }
        if (64 <= frameType && frameType <= 127) {
            // same_locals_1_stack_item_frame
            stack = new VerificationType[1];
            stack[0] = new VerificationType(in);
            return;
        }
        if (frameType == 247) {
            // same_locals_1_stack_item_frame_extended
            offsetDelta = in.readUnsignedShort();
            stack = new VerificationType[1];
            stack[0] = new VerificationType(in);
            return;
        }
        if (248 <= frameType && frameType <= 251) {
            // chop_frame & same_frame_extended
            offsetDelta = in.readUnsignedShort();
        }
        if (252 <= frameType && frameType <= 254) {
            offsetDelta = in.readUnsignedShort();
            locals = new VerificationType[frameType - 251];
            for (int i = 0; i < locals.length; ++i) {
                locals[i] = new VerificationType(in);
            }
        }
        if (frameType == 255) {
            offsetDelta = in.readUnsignedShort();
            locals = new VerificationType[in.readUnsignedShort()];
            for (int i = 0; i < locals.length; ++i) {
                locals[i] = new VerificationType(in);
            }
            stack = new VerificationType[in.readUnsignedShort()];
            for (int i = 0; i < stack.length; ++i) {
                stack[i] = new VerificationType(in);
            }
        }
    }

    /**
     * Returns Frame Type.
     * @return the frameType
     */
    public int getFrameType() {
        return frameType;
    }

    /**
     * Returns Offset Delta.
     * @return the offsetDelta
     */
    public int getOffsetDelta() {
        return offsetDelta;
    }

    /**
     * Returns Locals.
     * @return the locals
     */
    public VerificationType[] getLocals() {
        return locals;
    }

    /**
     * Returns Stack.
     * @return the stack
     */
    public VerificationType[] getStack() {
        return stack;
    }

    /**
     * Reads StackMapTable from class.
     * @param in the DataInputStream
     * @return StackMapTable
     * @throws IOException exception
     * @see Attribute
     */
    static StackMapFrame[] readStackMapTable(DataInputStream in)
        throws IOException {
        StackMapFrame[] table = new StackMapFrame[in.readUnsignedShort()];
        for (int i = 0; i < table.length; ++i) {
            table[i] =  new StackMapFrame(in);
        }
        return table;
    }
}
